package org.yoki.edu.bg.radius.filter.app.receiver;

import org.yoki.edu.bg.radius.filter.app.obs.SynObservable;
import org.yoki.edu.bg.radius.filter.app.receiver.packet.RadiusPacket;
import org.yoki.edu.bg.radius.filter.app.transit.SynPacketTransit;
import lombok.Getter;
import lombok.Setter;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author Sky$
 * @Description: TODO
 * @date 2018/1/3$ 14:08$
 */
public abstract class RadiusPacketReceiver extends SynObservable<SynPacketTransit> implements Runnable {

    @Getter
    @Setter
    protected ThreadPoolExecutor executor;

    @Setter
    @Getter
    public boolean stopFlag = false;

    public RadiusPacketReceiver() {
    }

    public RadiusPacketReceiver(SynPacketTransit observable) {
        addObserver(observable);
    }

    public abstract int getListenerPort();

    @Override
    public void run() {

        int port = getListenerPort();
        DatagramSocket socket = null;
        try {
            socket = new DatagramSocket(port);
        } catch (SocketException e) {
            e.printStackTrace();
        }
        byte[] buf = new byte[0XFF00];
        //循环接收来自设定端口的UDP数据包
        while (!stopFlag) {
            DatagramPacket packet = new DatagramPacket(buf, buf.length);
            System.out.println("Prepare to  listening port : " + port);
            try {
                socket.receive(packet);
                System.out.println("") ;
                System.out.println("") ;
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("An error occurred while listening port : " + port + " ! The system will be exit !");
                System.exit(0);
            }
            try {
                Long startTime = System.currentTimeMillis();
                Long tempTime = startTime;
                //转发给中转程序前的处理
                RadiusPacket clientRadiusPacket = beforeNotify(packet);
//                System.out.println("The \"BeforeNotify\" method deals with packages consuming " + (System.currentTimeMillis() - tempTime) + " ms");
                tempTime = System.currentTimeMillis();
                //将接收到的数据包转发给中转程序进行分发
                setChanged();
                notifyObservers(clientRadiusPacket);
//                System.out.println("The \"Transit Request\" method deals with packages consuming " + (System.currentTimeMillis() - tempTime) + " ms");
                tempTime = System.currentTimeMillis();
                //转发给中转程序后的后续处理，并返回服务器处理后的数据包
                RadiusPacket serverRadiusPacket = afterNotify(clientRadiusPacket);
//                System.out.println("The \"AfterNotify\" method deals with packages consuming " + (System.currentTimeMillis() - tempTime) + " ms");
                tempTime = System.currentTimeMillis();
                if (null != serverRadiusPacket) {
                    //将接收到的数据包转发给中转程序进行分发
                    setChanged();
                    notifyObservers(clientRadiusPacket, serverRadiusPacket);
//                    System.out.println("The \"Transit Response\" method deals with packages consuming " + (System.currentTimeMillis() - tempTime) + " ms");
                    tempTime = System.currentTimeMillis();
                }
                System.out.println("Total consuming " + (System.currentTimeMillis() - startTime) + " ms");
            } catch (Exception e) {
                System.out.println("An error occurred while deals with packages !!!");
                e.printStackTrace();
                exceptionHandle(packet, e);
            }

        }

    }

    /**
     * 接收到的UDP数据包转发给中转程序前的后续处理方法<br></br>
     * 由子类进行实现
     *
     * @param packet UDP数据包
     * @return
     */
    protected abstract RadiusPacket beforeNotify(DatagramPacket packet);

    /**
     * 接收到的UDP数据包转发给中转程序后的后续处理方法<br></br>
     * 由子类进行实现
     *
     * @param packet UDP数据包
     */
    protected abstract RadiusPacket afterNotify(RadiusPacket packet);

    protected abstract void exceptionHandle(DatagramPacket packet, Exception e);

}
